<template>
  <div ref="listRef" class="virtual-list" :style="{ height: listHeight }" @scroll="handleScroll">
    <div :style="{ height: totalHeight }" class="virtual-list__placeholder"></div>
    <div
      class="virtual-list__content"
      :style="{ transform: `translateY(${scrollTop}px)` }"
    >
      <template v-for="(item, index) in visibleData" :key="index">
        <slot :item="item" :index="index + startIndex"></slot>
      </template>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted, watch, toRefs } from 'vue';

const props = defineProps({
  // 列表数据
  data: {
    type: Array,
    required: true,
  },
  // 列表高度
  height: {
    type: String,
    default: '300px',
  },
  // 每个列表项的高度
  itemHeight: {
    type: Number,
    default: 30,
  },
});

const { data, height, itemHeight } = toRefs(props);

const listRef = ref<HTMLDivElement | null>(null);
const scrollTop = ref(0);
const startIndex = ref(0);
const endIndex = ref(0);
const visibleData = ref([]);

// 确保 data.value 和 itemHeight.value 不为 undefined
const totalHeight = data.value && itemHeight.value 
  ? `${data.value.length * itemHeight.value}px` 
  : '0px';
const listHeight = height.value || '300px';

const calculateVisibleRange = () => {
  if (listRef.value && data.value && itemHeight.value) {
    const { scrollTop: currentScrollTop } = listRef.value;
    scrollTop.value = currentScrollTop;
    startIndex.value = Math.floor(currentScrollTop / itemHeight.value);
    endIndex.value = startIndex.value + Math.ceil(parseInt(listHeight) / itemHeight.value);
    visibleData.value = data.value.slice(startIndex.value, endIndex.value);
  }
};

const handleScroll = () => {
  calculateVisibleRange();
};

onMounted(() => {
  calculateVisibleRange();
});

watch(
  data,
  () => {
    calculateVisibleRange();
  }
);
</script>

<style scoped>
.virtual-list {
  overflow-y: auto;
  position: relative;
}

.virtual-list__placeholder {
  width: 100%;
}

.virtual-list__content {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
}
</style>